package com.squid.base.utils;

import cn.hutool.core.net.NetUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.squid.base.common.enums.CommonErrorEnums;
import com.squid.base.common.error.ExceptionFactory;
import com.squid.base.utils.system.System;
import com.squid.base.utils.system.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * 系统监控utils
 *
 * @author jinhang1
 * @date 2024/07/24
 */
@Slf4j
@Component
public class SystemMonitorUtils {
    private static final int OSHI_WAIT_SECOND = 1000;
    private final String SYSTEM_MONITOR_PREFIX = "SYSTEM_MONITOR";
    @Resource
    private RedissonRedisUtils redissonRedisUtils;

    private ServerInfo copyTo() {
        SystemInfo si = new SystemInfo();
        HardwareAbstractionLayer hal = si.getHardware();
        Cpu cpu = setCpuInfo(hal.getProcessor());
        Memory memory = setMemInfo(hal.getMemory());
        System system = setSysInfo();
        Jvm jvm = setJvmInfo();
        List<SystemFile> systemFiles = setSysFiles(si.getOperatingSystem());
        ServerInfo serverInfo = new ServerInfo();
        serverInfo.setCpu(cpu);
        serverInfo.setMem(memory);
        serverInfo.setJvm(jvm);
        serverInfo.setSys(system);
        serverInfo.setSysFiles(systemFiles);
        return serverInfo;
    }

    public ServerInfo getServerInfo() {
        ServerInfo serverInfo = null;
        String lockKey = IpUtils.getIp();
        try {
            if (!redissonRedisUtils.tryLock(lockKey, 10000, 10000, TimeUnit.SECONDS)) {
                throw ExceptionFactory.instanceServerException(CommonErrorEnums.BUSY);
            }
            String key = SYSTEM_MONITOR_PREFIX + IpUtils.getIp();
            if (redissonRedisUtils.checkKey(SYSTEM_MONITOR_PREFIX + IpUtils.getIp())) {
                serverInfo = JSONObject.parseObject(redissonRedisUtils.getStringKey(key), ServerInfo.class);
            } else {
                serverInfo = copyTo();
                redissonRedisUtils.setExpiredStringKey(key, JSON.toJSONString(serverInfo), 60 * 24);
            }
        } catch (Exception e) {
            throw ExceptionFactory.instanceServerException("获取服务器信息失败", e);
        } finally {
            redissonRedisUtils.unlock(lockKey);
        }
        return serverInfo;
    }

    /**
     * 单位转换
     */
    private static String formatByte(long byteNumber) {
        //换算单位
        double FORMAT = 1024.0;
        double kbNumber = byteNumber / FORMAT;
        if (kbNumber < FORMAT) {
            return new DecimalFormat("#.##KB").format(kbNumber);
        }
        double mbNumber = kbNumber / FORMAT;
        if (mbNumber < FORMAT) {
            return new DecimalFormat("#.##MB").format(mbNumber);
        }
        double gbNumber = mbNumber / FORMAT;
        if (gbNumber < FORMAT) {
            return new DecimalFormat("#.##GB").format(gbNumber);
        }
        double tbNumber = gbNumber / FORMAT;
        return new DecimalFormat("#.##TB").format(tbNumber);
    }

    /**
     * 设置CPU信息
     */
    private Cpu setCpuInfo(CentralProcessor processor) {
        Cpu cpu = new Cpu();
        // CPU信息
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        Util.sleep(OSHI_WAIT_SECOND);
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
        cpu.setCpuNum(String.valueOf(processor.getLogicalProcessorCount()));
        cpu.setTotal(String.valueOf(totalCpu));
        cpu.setSys(new DecimalFormat("#.##%").format(cSys * 1.0 / totalCpu));
        cpu.setUsed(new DecimalFormat("#.##%").format(user * 1.0 / totalCpu));
        cpu.setWait(new DecimalFormat("#.##%").format(iowait * 1.0 / totalCpu));
        cpu.setFree(new DecimalFormat("#.##%").format(idle * 1.0 / totalCpu));
        return cpu;
    }

    /**
     * 设置服务器信息
     */
    private System setSysInfo() {
        System sys = new System();
        Properties props = java.lang.System.getProperties();
        sys.setComputerName(IpUtils.getHostName());
        sys.setComputerIp(NetUtil.getLocalhostStr());
        sys.setOsName(props.getProperty("os.name"));
        sys.setOsArch(props.getProperty("os.arch"));
        sys.setUserDir(props.getProperty("user.dir"));
        return sys;
    }

    /**
     * 设置内存信息
     */
    private Memory setMemInfo(GlobalMemory memory) {
        Memory mem = new Memory();
        //总内存
        long totalByte = memory.getTotal();
        //剩余
        long acaliableByte = memory.getAvailable();
        mem.setTotal(formatByte(totalByte));
        mem.setUsed(formatByte(totalByte - acaliableByte));
        mem.setFree(formatByte(acaliableByte));
        mem.setUsageRate(new DecimalFormat("#.##%").format((totalByte - acaliableByte) * 1.0 / totalByte));
        return mem;
    }

    /**
     * 设置Java虚拟机
     */
    private Jvm setJvmInfo() {
        Jvm jvm = new Jvm();
        Properties props = java.lang.System.getProperties();
        jvm.setTotal(String.valueOf(Runtime.getRuntime().totalMemory()));
        jvm.setMax(String.valueOf(Runtime.getRuntime().maxMemory()));
        jvm.setFree(String.valueOf(Runtime.getRuntime().freeMemory()));
        jvm.setVersion(props.getProperty("java.version"));
        jvm.setHome(props.getProperty("java.home"));
        jvm.setUsageRate(new DecimalFormat("#.##%")
                .format((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())
                        * 1.0 / Runtime.getRuntime().totalMemory()));
        return jvm;
    }

    /**
     * 字节转换
     *
     * @param size 字节大小
     * @return 转换后值
     */
    public String convertFileSize(long size) {
        long kb = 1024;
        long mb = kb * 1024;
        long gb = mb * 1024;
        if (size >= gb) {
            return String.format("%.1f GB", (float) size / gb);
        } else if (size >= mb) {
            float f = (float) size / mb;
            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
        } else if (size >= kb) {
            float f = (float) size / kb;
            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
        } else {
            return String.format("%d B", size);
        }
    }

    /**
     * 设置磁盘信息
     */
    private List<SystemFile> setSysFiles(OperatingSystem os) {
        List<SystemFile> sysFiles = new LinkedList<SystemFile>();
        FileSystem fileSystem = os.getFileSystem();
        List<OSFileStore> fileStores = fileSystem.getFileStores();
        for (OSFileStore fs : fileStores) {
            long free = fs.getUsableSpace();
            long total = fs.getTotalSpace();
            long used = total - free;
            SystemFile sysFile = new SystemFile();
            sysFile.setDirName(fs.getMount());
            sysFile.setSysTypeName(fs.getType());
            sysFile.setTypeName(fs.getName());
            sysFile.setTotal(formatByte(total));
            sysFile.setFree(formatByte(free));
            sysFile.setUsed(formatByte(used));
            if (fs.getTotalSpace() == 0) {
                //资源的使用率
                sysFile.setUsage("0");
            } else {
                sysFile.setUsage(new DecimalFormat("#.##%").format((fs.getTotalSpace() - fs.getUsableSpace()) * 1.0 / fs.getTotalSpace()));
            }
            sysFiles.add(sysFile);
        }
        return sysFiles;
    }
}
